/*-------------------------------------------------------------------------
 *
 * atomics.c
 *	   原子实现的非内联部分
 *
 * Portions Copyright (c) 2013-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/port/atomics.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "miscadmin.h"
#include "port/atomics.h"
#include "storage/spin.h"

#ifdef PG_HAVE_MEMORY_BARRIER_EMULATION
#ifdef WIN32
#error "barriers are required (and provided) on WIN32 platforms"
#endif
#include <signal.h>
#endif

#ifdef PG_HAVE_MEMORY_BARRIER_EMULATION
void pg_spinlock_barrier(void)
{
	/*
	 * 注意：我们在这里必须是可重入的，因为某些屏障放置在信号处理程序中。
	 *
	 * 我们使用kill(0)作为后备屏障，因为我们假设在需要后备屏障支持的旧系统上，
	 * 在检查postmaster pid的存在时会包含一个适当的屏障。
	 */
	(void) kill(PostmasterPid, 0);
}
#endif

#ifdef PG_HAVE_COMPILER_BARRIER_EMULATION
void pg_extern_compiler_barrier(void)
{
	/* 不执行任何操作 */
}
#endif


#ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION

void pg_atomic_init_flag_impl(volatile pg_atomic_flag *fc_ptr)
{
	StaticAssertStmt(sizeof(fc_ptr->sema) >= sizeof(slock_t),
					 "size mismatch of atomic_flag vs slock_t");

#ifndef HAVE_SPINLOCKS

	/*
	 * 注意：如果我们使用基于信号量的TAS仿真，请务必使用一组单独的信号量。
	 * 否则，如果在持有自旋锁时操作原子变量，我们会遇到麻烦。
	 */
	s_init_lock_sema((slock_t *) &fc_ptr->sema, true);
#else
	SpinLockInit((slock_t *) &fc_ptr->sema);
#endif

	fc_ptr->value = false;
}

bool pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *fc_ptr)
{
	uint32		oldval;

	SpinLockAcquire((slock_t *) &fc_ptr->sema);
	oldval = fc_ptr->value;
	fc_ptr->value = true;
	SpinLockRelease((slock_t *) &fc_ptr->sema);

	return oldval == 0;
}

void pg_atomic_clear_flag_impl(volatile pg_atomic_flag *fc_ptr)
{
	SpinLockAcquire((slock_t *) &fc_ptr->sema);
	fc_ptr->value = false;
	SpinLockRelease((slock_t *) &fc_ptr->sema);
}

bool pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *fc_ptr)
{
	return fc_ptr->value == 0;
}

#endif							/* PG_HAVE_ATOMIC_FLAG_SIMULATION */

#ifdef PG_HAVE_ATOMIC_U32_SIMULATION
void pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *fc_ptr, uint32 fc_val_)
{
	StaticAssertStmt(sizeof(fc_ptr->sema) >= sizeof(slock_t),
					 "size mismatch of atomic_uint32 vs slock_t");

	/*
	 * 如果我们使用基于信号量的原子标志，请注意在持有自旋锁时的嵌套使用。
	 */
#ifndef HAVE_SPINLOCKS
	s_init_lock_sema((slock_t *) &fc_ptr->sema, true);
#else
	SpinLockInit((slock_t *) &fc_ptr->sema);
#endif
	fc_ptr->value = fc_val_;
}

void pg_atomic_write_u32_impl(volatile pg_atomic_uint32 *fc_ptr, uint32 fc_val)
{
	/*
	 * 也许人们会认为未锁定的写入不需要获取自旋锁，但这样的想法是错误的。
	 * 即使是未锁定的写入也必须导致并发的pg_atomic_compare_exchange_u32()（等等）失败。
	 */
	SpinLockAcquire((slock_t *) &fc_ptr->sema);
	fc_ptr->value = fc_val;
	SpinLockRelease((slock_t *) &fc_ptr->sema);
}

bool pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *fc_ptr,
									uint32 *fc_expected, uint32 fc_newval)
{
	bool		fc_ret;

	/*
	 * 在自旋锁下执行原子操作。看起来如果锁不可用，我们可以跳过cmpxchg，
	 * 但那只是模拟了一种“弱”的比较与交换。即允许虚假失败的比较与交换。
	 * 由于几种算法依赖于强变体，并且在大多数主要架构上能够高效实现，
	 * 所以我们在这里也模拟它。
	 */
	SpinLockAcquire((slock_t *) &fc_ptr->sema);

	/* 执行比较/交换逻辑 */
	fc_ret = fc_ptr->value == *fc_expected;
	*fc_expected = fc_ptr->value;
	if (fc_ret)
		fc_ptr->value = fc_newval;

	/* 并释放锁 */
	SpinLockRelease((slock_t *) &fc_ptr->sema);

	return fc_ret;
}

uint32 pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *fc_ptr, int32 add_)
{
	uint32		fc_oldval;

	SpinLockAcquire((slock_t *) &fc_ptr->sema);
	fc_oldval = fc_ptr->value;
	fc_ptr->value += add_;
	SpinLockRelease((slock_t *) &fc_ptr->sema);
	return fc_oldval;
}

#endif							/* PG_HAVE_ATOMIC_U32_SIMULATION */


#ifdef PG_HAVE_ATOMIC_U64_SIMULATION

void pg_atomic_init_u64_impl(volatile pg_atomic_uint64 *fc_ptr, uint64 fc_val_)
{
	StaticAssertStmt(sizeof(fc_ptr->sema) >= sizeof(slock_t),
					 "size mismatch of atomic_uint64 vs slock_t");

	/*
	 * 如果我们使用基于信号量的原子标志，请注意在持有自旋锁时的嵌套使用。
	 */
#ifndef HAVE_SPINLOCKS
	s_init_lock_sema((slock_t *) &fc_ptr->sema, true);
#else
	SpinLockInit((slock_t *) &fc_ptr->sema);
#endif
	fc_ptr->value = fc_val_;
}

bool pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *fc_ptr,
									uint64 *fc_expected, uint64 fc_newval)
{
	bool		fc_ret;

	/*
	 * 在自旋锁下执行原子操作。看起来如果锁不可用，我们可以跳过cmpxchg，
	 * 但那只是模拟了一种“弱”的比较与交换。即允许虚假失败的比较与交换。
	 * 由于几种算法依赖于强变体，并且在大多数主要架构上能够高效实现，
	 * 所以我们在这里也模拟它。
	 */
	SpinLockAcquire((slock_t *) &fc_ptr->sema);

	/* 执行比较/交换逻辑 */
	fc_ret = fc_ptr->value == *fc_expected;
	*fc_expected = fc_ptr->value;
	if (fc_ret)
		fc_ptr->value = fc_newval;

	/* 并释放锁 */
	SpinLockRelease((slock_t *) &fc_ptr->sema);

	return fc_ret;
}

uint64 pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *fc_ptr, int64 add_)
{
	uint64		fc_oldval;

	SpinLockAcquire((slock_t *) &fc_ptr->sema);
	fc_oldval = fc_ptr->value;
	fc_ptr->value += add_;
	SpinLockRelease((slock_t *) &fc_ptr->sema);
	return fc_oldval;
}

#endif							/* PG_HAVE_ATOMIC_U64_SIMULATION */
